/*
 * RestHttpHandler.h
 *
 *  Created on: 2018年2月9日
 *      Author: terry
 */

#ifndef RESTHTTPHANDLER_H_
#define RESTHTTPHANDLER_H_

#include "HttpServer.h"
#include "HttpUserStore.h"
#include <functional>
#include <map>
#include <memory>
#include "HttpTokenAuth.h"
#include "CodeDict.h"


typedef std::shared_ptr< HttpUserStore >    HttpUserStorePtr;


class RestHttpHandler : public HttpHandler
{
public:
	typedef std::function< int(HttpRequest&, HttpResult& result) > 	HttpProcessor;

public:
	RestHttpHandler();
	virtual ~RestHttpHandler();

	virtual int handle(HttpRequest& request, HttpResult& result);

	void setUserStore(HttpUserStorePtr store);

	HttpTokenAuth* getAuth();

	bool putToken(const std::string& token, const std::string& username, int expiration);


	void setup(const std::string& url, HttpProcessor proc);

	void setup(const std::string& url, bool acquireAuth, HttpProcessor proc);

	void setup(const std::string& url, const std::string& method, HttpProcessor proc);
	void setup(const std::string& url, const char* method, HttpProcessor proc);

	void setup(const std::string& url, const std::string& method, bool acquireAuth, HttpProcessor proc);
	void setup(const std::string& url, const char* method, bool acquireAuth, HttpProcessor proc);

	int handleJsonApi(HttpRequest& request, HttpResult& result);

	bool touch(HttpRequest& request, HttpResult& result);
	bool checkExpired(HttpRequest& request, HttpResult& result);
	bool checkAuth(HttpRequest& request, HttpResult& result);

	int login(HttpRequest& request, HttpResult& result, std::string& username, std::string& token);
	int logout(HttpRequest& request, HttpResult& result);

	
	template < class TMember, class T>
	void route(const std::string& url, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, proc);
	}

	template < class TMember, class T>
	void route(const std::string& url, const char* method, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, method, proc);
	}

	template < class TMember, class T>
	void route(const std::string& url, const char* method, bool acquireAuth,  TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, method, acquireAuth, proc);
	}



	template < class TMember, class T>
	void get(const std::string& url, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::GET, proc);
	}

	template < class TMember, class T>
	void get(const std::string& url, bool acquireAuth, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::GET, acquireAuth, proc);
	}


	template < class TMember, class T>
	void post(const std::string& url, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::POST, proc);
	}

	template < class TMember, class T>
	void post(const std::string& url, bool acquireAuth, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::POST, acquireAuth, proc);
	}



	template < class TMember, class T>
	void put(const std::string& url, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::PUT, proc);
	}

	template < class TMember, class T>
	void put(const std::string& url, bool acquireAuth, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::POST, acquireAuth, proc);
	}


	template < class TMember, class T>
	void del(const std::string& url, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::DELETE, proc);
	}

	template < class TMember, class T>
	void del(const std::string& url, bool acquireAuth, TMember m, T* t)
	{
		HttpProcessor proc = std::bind(m, t, std::placeholders::_1, std::placeholders::_2);
		setup(url, HttpMethod::DELETE, acquireAuth, proc);
	}


	bool checkAuth(HttpRequest& request);

	int handleAuth(HttpRequest& request, HttpResult& result);

public:
	bool loadCode(const std::string& filepath);

	std::string getCode(int code, const char* defValue = "");

	void setCheckExpired(HttpServer* server, int seconds);

	void checkExpired();

public:
	int setError(int code, HttpResult& result);

	int setError(int code, const std::string& message, HttpResult& result);

	static int setError(int code, const std::string& message, std::string& jsonText);

	static std::string makeResult(int code, const std::string& message);

protected:
	bool getToken(HttpRequest& request, std::string& token);

	bool getUser(HttpRequest& request, std::string& username);

protected:
	struct ProcessorEntry
	{
		std::string url;
		std::string method;
		bool auth;
		HttpProcessor processor;

		ProcessorEntry():
			auth()
		{
		}

		ProcessorEntry(const std::string& url, const std::string& method, HttpProcessor processor):
			auth()
		{
			this->url = url;
			this->method = method;
			this->processor = processor;
		}
	};

	typedef std::map< std::string, ProcessorEntry >	HttpProcessorMap;
	typedef std::map< std::string, HttpProcessorMap> ProcessorTable;

	bool findProcessor(HttpRequest& request, ProcessorEntry& entry);

	bool matchEntry(HttpRequest& request, const std::string& pattern, StringMap& params);

	ProcessorTable::const_iterator findEntry(HttpRequest& request, StringMap& params);

protected:
	HttpTokenAuth*  m_auth;
	ProcessorTable	m_procTable;

	CodeDict	m_codeDict;

};

#endif /* RESTHTTPHANDLER_H_ */
